package cn.itcast.zhoumingxin.servlet;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.itcast.zhoumingxin.annotation.EnjoryAutowired;
import cn.itcast.zhoumingxin.annotation.EnjoryController;
import cn.itcast.zhoumingxin.annotation.EnjoryRequestMapping;
import cn.itcast.zhoumingxin.annotation.EnjoryRequestParam;
import cn.itcast.zhoumingxin.annotation.EnjoryService;
import cn.itcast.zhoumingxin.controller.JamesController;

public class DispathcherServlet extends HttpServlet {

	List<String> classNames = new ArrayList<>();

	Map<String, Object> beans = new HashMap<>();

	Map<String, Object> handerMap = new HashMap<>();

	// <load-on-startup>0</load-on-startup>,会优先初始化init
	public void init(ServletConfig config) {
		// 扫描com.enjoy.james.controller.JamesController Class<?>
		doScan("cn.itcast.zhoumingxin");

		doInstance();

		doAutowired();

		urlMapping();

	}

	public void urlMapping() {
		for (Map.Entry<String, Object> entry : beans.entrySet()) {
			Object instance = entry.getValue();
			Class<?> clazz = instance.getClass();
			if (clazz.isAnnotationPresent(EnjoryController.class)) {
				EnjoryRequestMapping reqMapping = clazz.getAnnotation(EnjoryRequestMapping.class);
				String classPath = reqMapping.value();
				Method[] methods = clazz.getMethods();
				for (Method method : methods) {
					if (method.isAnnotationPresent(EnjoryRequestMapping.class)) {
						EnjoryRequestMapping reqMapping1 = method.getAnnotation(EnjoryRequestMapping.class);
						String methodPath = reqMapping1.value();

						handerMap.put(classPath + methodPath, method);
					} else {
						continue;
					}
				}
			}
		}

	}

	public void doAutowired() {

		for (Map.Entry<String, Object> entry : beans.entrySet()) {
			Object instance = entry.getValue();
			Class<?> clazz = instance.getClass();

			if (clazz.isAnnotationPresent(EnjoryController.class)) {
				Field[] fileds = clazz.getDeclaredFields();
				for (Field field : fileds) {
					if (field.isAnnotationPresent(EnjoryAutowired.class)) {
						EnjoryAutowired annotation = field.getAnnotation(EnjoryAutowired.class);
						String key = annotation.value();
						Object value = beans.get(key);
						field.setAccessible(true);
						try {
							field.set(instance, value);
						} catch (IllegalArgumentException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (IllegalAccessException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			} else {
				continue;
			}
		}
	}

	public void doInstance() {
		for (String string : classNames) {
			String cn = string.replace(".class", "");

			try {
				Class<?> clazz = Class.forName(cn);
				// 解析类上得注解
				if (clazz.isAnnotationPresent(EnjoryController.class)) {
					Object value = clazz.newInstance();

					// map.put(key,value)
					EnjoryRequestMapping reqMap = clazz.getAnnotation(EnjoryRequestMapping.class);
					String key = reqMap.value();
					beans.put(key, value);

				} else if (clazz.isAnnotationPresent(EnjoryService.class)) {
					Object value = clazz.newInstance();

					// map.put(key,value)
					EnjoryService service = clazz.getAnnotation(EnjoryService.class);
					String key = service.value();
					beans.put(key, value);
				} else {
					continue;
				}
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	// 扫描
	public void doScan(String basePackage) {
		URL url = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/"));
		// url报空指针呢
		String fileStr = url.getFile();
		File file = new File(url.getFile());
		getFileClass(file, basePackage);
	}

	private void getFileClass(File file, String basePackage) {
		File[] listFiles = file.listFiles();
		for (File file2 : listFiles) {
			if (file2.isDirectory()) {
				getFileClass(file2, basePackage+"."+file2.getName());
			} else {
				classNames.add(basePackage + "." + file2.getName());
			}
		}

	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		this.doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// 获取请求路径
		String requestURI = req.getRequestURI();
		String context = req.getContextPath();

		String path = requestURI.replace(context, "");

		Method method = (Method) handerMap.get(path);

		JamesController instance = (JamesController) beans.get("/" + path.split("/")[1]);

		Object args[] = hand(req, resp, method);

		try {
			method.invoke(instance, args);
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// 进行参数封装,策略模式改进
	public Object[] hand(HttpServletRequest request, HttpServletResponse response, Method method) {

		Class<?>[] paramClazzs = method.getParameterTypes();

		Object[] args = new Object[paramClazzs.length];

		int args_i = 0;
		int index = 0;
		for (Class<?> paramClazz : paramClazzs) {
			if (ServletRequest.class.isAssignableFrom(paramClazz)) {
				args[args_i++] = request;
			}
			if (ServletResponse.class.isAssignableFrom(paramClazz)) {
				args[args_i++] = response;
			}
			// 从0-3判断有没有RequestParam注解，很明显paramClazz为0和1，不是
			// 当为2和3时为@RequestParam，需要解析
			// [@com.enjoy.james.annotation.EnjoyRequestParam(value=name)]
			Annotation[] paramAns = method.getParameterAnnotations()[index];
			if (paramAns.length > 0) {
				for (Annotation paramAn : paramAns) {
					if (EnjoryRequestParam.class.isAssignableFrom(paramAn.getClass())) {
						EnjoryRequestParam rp = (EnjoryRequestParam) paramAn;
						// 找到注解里得name和age
						args[args_i++] = request.getParameter(rp.value());
					}
				}
			}
			index++;
		}

		return args;
	}

}
